package backend

import (
	"time"
	"sync"
	"proxy"
)
const kRpcIdMax = 1000000
const kRpcTimeout = time.Second * 5

type RpcMap struct {
	mu        sync.Mutex
	lastRpcId int
	rpcById   map[int]*RpcCall
}

func NewRpcMap() *RpcMap {
	return &RpcMap{
		lastRpcId: 0,
		rpcById:   make(map[int]*RpcCall),
	}
}

func (m *RpcMap) NewCall() *RpcCall {
	call := &RpcCall{
		rpcMap:   m,
		response: make(chan *Message, 1),
		timer:    time.NewTimer(kRpcTimeout),
	}
	m.mu.Lock()

	m.lastRpcId = (m.lastRpcId+1) % kRpcIdMax

	call.id = m.lastRpcId

	m.rpcById[call.id] = call

	m.mu.Unlock()

	return call
}

func (m *RpcMap) FinishCall(rpcId int) *RpcCall {
	m.mu.Lock()

	rpc := m.rpcById[rpcId]

	delete(m.rpcById, rpcId)

	m.mu.Unlock()

	return rpc
}

type RpcCall struct {
	rpcMap   *RpcMap
	id       int
	response chan *Message
	timer    *time.Timer
}

func (c *RpcCall) Cancel() {
	c.rpcMap.mu.Lock()
	delete(c.rpcMap.rpcById, c.id)
	c.rpcMap.mu.Unlock()
}

func (c *RpcCall) GetId() int {
	return c.id
}

func (c *RpcCall) WaitForResponse() (*Message, error) {
	select {
	case <-c.timer.C:
		return nil, proxy.ErrTimeout
	case rsp := <-c.response:
		c.timer.Stop()
		return rsp, nil
	}
}

func (c *RpcCall) NotifyResponse(msg *Message) {
	select {
	case c.response <- msg:
	case <- c.timer.C:
	}
}
